package q2d.client.controllers;

import java.awt.Point;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.io.ByteArrayInputStream;
import java.io.InputStream;

import javax.sound.sampled.AudioFormat;

import q2d.client.renderers.GFactory;
import q2d.client.worlds.GameWorld;
import q2d.common.CollisionDetector;
import q2d.common.models.Map;
import q2d.common.models.units.Bullet;
import q2d.common.models.units.Player;

import com.brackeen.javagamebook.input.GameAction;
import com.brackeen.javagamebook.input.InputManager;
import com.brackeen.javagamebook.sound.*;

/** PlayerController:
 * Kontroler wejscia z klawiatury i myszy. Steruje ruchem gracza.
 *
 */
public class PlayerController extends Controller {

	private final int SHOOT_DELAY = 300;

	public static final int PLAYER_SPEED_Y = 9;
	public static final int PLAYER_SPEED_X = 9;

	// referencja swiat a gry:
	private GameWorld gameWorld;
	// referencja do obiektu InputManagera
	private InputManager playerInput;
	// detektor kolizji
	private CollisionDetector collisionDetector;
	// referencja do FXcontroller'a
	private FXController fxController;
	// referencja do BulletsControllera
	private BulletsController bulletsController;
	// licznik smierci gracza
	private int lastDeathsCounterValue;
	// opoznienie kolejnego wystrzalu
	private int lastShootTime = SHOOT_DELAY;
	
	//dzwieki


	private SoundManager soundManager;
	private Sound shoot;
	private boolean soundEnabled;

	

	// sterowanie
	private GameAction goUpAction;
	private GameAction goDownAction;
	private GameAction goLeftAction;
	private GameAction goRightAction;
	private GameAction shootAction;


	/**
	 * Konstruktor.
	 * @param input_manager
	 * @param game_world
	 * @param game_width
	 * @param game_height
	 */
	public PlayerController(int game_width, int game_height, InputManager input_manager, GameWorld game_world, BulletsController bullets_controller, FXController fx_controller,SoundManager sound_Controller){
		super(game_width, game_height);
		playerInput = input_manager;
		gameWorld = game_world;
		fxController = fx_controller;
		bulletsController = bullets_controller;
		lastDeathsCounterValue = 0;		
		collisionDetector = new CollisionDetector();
		soundManager = sound_Controller;
		
		shoot = soundManager.getSound(GFactory.SoundsPath+"shoot.wav");
		
	}


	@Override
	public void controll(long elapsed_time) {
		if (!isWorking) return;

		// ruch gracza
		if (goUpAction.isPressed()){
			if (!collisionDetector.isPlayerMapCollision(gameWorld.gameMap, gameWorld.thisPlayer.x, gameWorld.thisPlayer.y - PLAYER_SPEED_Y)){
				gameWorld.thisPlayer.y -= PLAYER_SPEED_Y;
			}
		}
		if (goDownAction.isPressed()) {
			if (!collisionDetector.isPlayerMapCollision(gameWorld.gameMap, gameWorld.thisPlayer.x, gameWorld.thisPlayer.y + PLAYER_SPEED_Y)){
				gameWorld.thisPlayer.y += PLAYER_SPEED_Y;
			}
		}
		if (goLeftAction.isPressed()) {
			if (!collisionDetector.isPlayerMapCollision(gameWorld.gameMap, gameWorld.thisPlayer.x - PLAYER_SPEED_X, gameWorld.thisPlayer.y)){
				gameWorld.thisPlayer.x -= PLAYER_SPEED_X;
			}
		}
		if (goRightAction.isPressed()) {
			if (!collisionDetector.isPlayerMapCollision(gameWorld.gameMap, gameWorld.thisPlayer.x + PLAYER_SPEED_X, gameWorld.thisPlayer.y)){
				gameWorld.thisPlayer.x += PLAYER_SPEED_X;
			}
		}

		// strzal
		if (shootAction.isPressed()) {
			if (lastShootTime<0){
				shoot();
				lastShootTime = SHOOT_DELAY;
			}
		}
		lastShootTime -= elapsed_time;

		// obrot gracza
		//int dx = playerInput.getMouseX() - screenCenterX - Player.BOUND_RADIUS;
		//int dy = playerInput.getMouseY() - screenCenterY - Player.BOUND_RADIUS;
		int dx = playerInput.getMouseX() - gameWorld.thisPlayer.screenX - Player.BOUND_RADIUS;
		int dy = playerInput.getMouseY() - gameWorld.thisPlayer.screenY - Player.BOUND_RADIUS;
		gameWorld.thisPlayer.rotation = (float)(Math.atan2(dy, dx) + Math.PI/2);
		//gameWorld.debugString = String.valueOf(gameWorld.thisPlayer.rotation*180/Math.PI + 90);

		// smierc
		if (lastDeathsCounterValue < gameWorld.thisPlayer.deathsCounter){
			Point new_position = setPosition(gameWorld.thisPlayer.team);
			gameWorld.thisPlayer.x = new_position.x;
			gameWorld.thisPlayer.y = new_position.y;
			lastDeathsCounterValue = gameWorld.thisPlayer.deathsCounter;
		}


	}


	@Override
	public void disableControll() {
		if (!isWorking) return;
		isWorking = false;

		playerInput.resetAllGameActions();
	}


	@Override
	public void enableControll() {
		if (isWorking) return;
		isWorking = true;

        goUpAction = new GameAction("goUp", GameAction.NORMAL);
        goDownAction = new GameAction("goDown", GameAction.NORMAL);
        goLeftAction = new GameAction("goLeft", GameAction.NORMAL);
        goRightAction = new GameAction("goRight", GameAction.NORMAL);
        shootAction = new GameAction("shoot", GameAction.NORMAL);

        playerInput.mapToKey(goUpAction, KeyEvent.VK_UP);
        playerInput.mapToKey(goUpAction, KeyEvent.VK_W);
        playerInput.mapToKey(goDownAction, KeyEvent.VK_DOWN);
        playerInput.mapToKey(goDownAction, KeyEvent.VK_S);
        playerInput.mapToKey(goLeftAction, KeyEvent.VK_LEFT);
        playerInput.mapToKey(goLeftAction, KeyEvent.VK_A);
        playerInput.mapToKey(goRightAction, KeyEvent.VK_RIGHT);
        playerInput.mapToKey(goRightAction, KeyEvent.VK_D);
        playerInput.mapToMouse(shootAction, InputManager.MOUSE_BUTTON_1);
	}

	
	
	
	/**
	 * Strzal z broni.
	 */
	private void shoot(){
		/* obliczenie punktu wylotu pocisku z lufy (uwzglednia obrot gracza)
		 *             [cosA  sinA 0]
		 * [x, y, 1] x [-sinA cosA 0] = [offsetX, offsetY, 1]
		 *             [ 0     0   1]
		 */
		float offsetX;
		float offsetY;
		float sinA = (float)Math.sin(gameWorld.thisPlayer.rotation);
		float cosA = (float)Math.cos(gameWorld.thisPlayer.rotation);
		offsetX = Player.WEAPON_1_BARREL_OFFSET_X*cosA - Player.WEAPON_1_BARREL_OFFSET_Y*sinA;
		offsetY = Player.WEAPON_1_BARREL_OFFSET_X*sinA + Player.WEAPON_1_BARREL_OFFSET_Y*cosA;

		// polozenie konca lufy
		double bx = gameWorld.thisPlayer.x + Player.BOUND_RADIUS + Math.round(offsetX);
		double by = gameWorld.thisPlayer.y + Player.BOUND_RADIUS + Math.round(offsetY);
		// obliczenie wektora predkosci pocisku (kierunku, wlasciwa
		// dlugosc wektora jest nadawana w kontrolerze BulletsController)
		double dx = playerInput.getMouseX() - gameWorld.thisPlayer.screenX - Player.BOUND_RADIUS;
		double dy = playerInput.getMouseY() - gameWorld.thisPlayer.screenY - Player.BOUND_RADIUS;

		// dodanie pocisku
		bulletsController.addBullet(bx, by, dx, dy, Bullet.TYPE_1);
		// animacja strzalu
		addShotAnimation();
		//dodanie dzwieku
		if(soundEnabled) soundManager.play(shoot);

		// dodanie FX'a
		fxController.addFxSmoke((int) bx, (int) by, dx, dy);

	}


	/**
	 * Losuje nowa pozycje gracza po zabiciu.
	 */
	public Point setPosition(int team){
		int deltaX = 0;
		int deltaY= 0;
		double tempX = 0;
		double tempY = 0;
		int StartPositionX = 0;
		int StartPositionY = 0;
		Point p = new Point();
		if(team == Player.BLUE_TEAM){
			deltaX = Map.X_RANGE_BLUE_END - Map.X_RANGE_BLUE_START;
			deltaY = Map.Y_RANGE_BLUE_END - Map.Y_RANGE_BLUE_START;
			StartPositionX = Map.X_RANGE_BLUE_START;
			StartPositionY = Map.Y_RANGE_BLUE_START;	
		}
		else if (team == Player.RED_TEAM){
			deltaX = Map.X_RANGE_RED_END - Map.X_RANGE_RED_START;
			deltaY = Map.Y_RANGE_RED_END - Map.Y_RANGE_RED_START;
			StartPositionX = Map.X_RANGE_RED_START;
			StartPositionY = Map.Y_RANGE_RED_START;
		}
		do{
			tempX = (deltaX*Math.random() + StartPositionX)*GFactory.TILE_WIDTH;
			tempY = (deltaY*Math.random() + StartPositionY)*GFactory.TILE_HEIGHT;
		}
		while(collisionDetector.isPlayerMapCollision(gameWorld.gameMap, tempX, tempY));
		
		p.x = (int)tempX;
		p.y = (int)tempY;
		return p;
	}


	/**
	 * Dodanie animacji wystrzalu
	 * 
	 * @param gameWorld referencja na GameWorld
	 */
	private void addShotAnimation(){
		if (gameWorld.thisPlayer.team == Player.BLUE_TEAM){
			if(gameWorld.thisPlayer.anim.framesAmount() <= GFactory.IMG_PLAYER_BLUE_ANIMATION_QUANTITY){
				gameWorld.thisPlayer.anim.addFrame(GFactory.IMG_PLAYER_BLUE_2, 2, true);
				gameWorld.thisPlayer.anim.addFrame(GFactory.IMG_PLAYER_BLUE_3, 2, true);
				gameWorld.thisPlayer.anim.addFrame(GFactory.IMG_PLAYER_BLUE_4, 2, true);
				gameWorld.thisPlayer.anim.addFrame(GFactory.IMG_PLAYER_BLUE_5, 2, true);
				gameWorld.thisPlayer.anim.addFrame(GFactory.IMG_PLAYER_BLUE_6, 2, true);
				gameWorld.thisPlayer.anim.addFrame(GFactory.IMG_PLAYER_BLUE_7, 2, true);
				gameWorld.thisPlayer.anim.addFrame(GFactory.IMG_PLAYER_BLUE_8, 2, true);
			}
		} else {
			if(gameWorld.thisPlayer.anim.framesAmount() <= GFactory.IMG_PLAYER_RED_ANIMATION_QUANTITY){
				gameWorld.thisPlayer.anim.addFrame(GFactory.IMG_PLAYER_RED_2, 2, true);
				gameWorld.thisPlayer.anim.addFrame(GFactory.IMG_PLAYER_RED_3, 2, true);
				gameWorld.thisPlayer.anim.addFrame(GFactory.IMG_PLAYER_RED_4, 2, true);
				gameWorld.thisPlayer.anim.addFrame(GFactory.IMG_PLAYER_RED_5, 2, true);
				gameWorld.thisPlayer.anim.addFrame(GFactory.IMG_PLAYER_RED_6, 2, true);
				gameWorld.thisPlayer.anim.addFrame(GFactory.IMG_PLAYER_RED_7, 2, true);
				gameWorld.thisPlayer.anim.addFrame(GFactory.IMG_PLAYER_RED_8, 2, true);
			}
		}
	}// koniec addShotAnimation
	public void setSound(boolean s){
		soundEnabled = s;
	}
}
